using DapperExtensionsNew.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace DapperExtensionsNew.Utils
{
	public class MethodInfoUtil
	{
		private delegate TValue ByRefFunc<TDeclaringType, TValue>(ref TDeclaringType arg);

		public static readonly Lazy<IEnumerable<MethodInfo>> StringConcatMethodInfos = new Lazy<IEnumerable<MethodInfo>>(() => from m in typeof(string).GetMethods()
			where m.Name == "Concat"
			select m);

		public static readonly MethodInfo StringBuilderToString = typeof(StringBuilder).GetMethod("ToString", new Type[0]);

		public static readonly MethodInfo StringBuilderAppend = typeof(StringBuilder).GetMethod("Append", new Type[1]
		{
			typeof(string)
		});

		private static BindingFlags BindingFlags = BindingFlags.Static | BindingFlags.NonPublic;

		public static readonly MethodInfo ReplaceMethod = typeof(MethodInfoUtil).GetMethod("Replace", BindingFlags);

		public static readonly MethodInfo SubStringMethod = typeof(MethodInfoUtil).GetMethod("SubString", BindingFlags);

		public static readonly MethodInfo DynanicParametersAddMethod = typeof(DynanicParameters).GetMethod("Add", new Type[2]
		{
			typeof(string),
			typeof(object)
		});

		private static readonly MethodInfo ChangeTypeMethod = typeof(Convert).GetMethod("ChangeType", new Type[2]
		{
			typeof(object),
			typeof(TypeCode)
		});

		private static readonly MethodInfo CallPropertyGetterOpenGenericMethod = typeof(MethodInfoUtil).GetTypeInfo().GetDeclaredMethod("CallPropertyGetter");

		private static readonly MethodInfo CallPropertyGetterByReferenceOpenGenericMethod = typeof(MethodInfoUtil).GetTypeInfo().GetDeclaredMethod("CallPropertyGetterByReference");

		private static string Replace(string oldstring, string patten, string newstring)
		{
			return oldstring.Replace(patten, newstring);
		}

		private static string SubString(string oldstring)
		{
			if (string.IsNullOrEmpty(oldstring))
			{
				return string.Empty;
			}
			return oldstring.Substring(0, oldstring.Length - 1);
		}

		private static object CallPropertyGetter<TDeclaringType, TValue>(Func<TDeclaringType, TValue> getter, object target)
		{
			return getter((TDeclaringType)target);
		}

		private static object CallPropertyGetterByReference<TDeclaringType, TValue>(ByRefFunc<TDeclaringType, TValue> getter, object target)
		{
			TDeclaringType arg = (TDeclaringType)target;
			return getter(ref arg);
		}

		public static Func<object, object> MakeFastPropertyGetter(PropertyInfo propertyInfo)
		{
			return MakeFastPropertyGetter(propertyInfo, CallPropertyGetterOpenGenericMethod, CallPropertyGetterByReferenceOpenGenericMethod);
		}

		private static Func<object, object> MakeFastPropertyGetter(PropertyInfo propertyInfo, MethodInfo propertyGetterWrapperMethod, MethodInfo propertyGetterByRefWrapperMethod)
		{
			MethodInfo getMethod = propertyInfo.GetMethod;
			if (getMethod.DeclaringType.GetTypeInfo().IsValueType)
			{
				return MakeFastPropertyGetter(typeof(ByRefFunc<, >), getMethod, propertyGetterByRefWrapperMethod);
			}
			return MakeFastPropertyGetter(typeof(Func<, >), getMethod, propertyGetterWrapperMethod);
		}

		private static Func<object, object> MakeFastPropertyGetter(Type openGenericDelegateType, MethodInfo propertyGetMethod, MethodInfo openGenericWrapperMethod)
		{
			Type declaringType = propertyGetMethod.DeclaringType;
			Type returnType = propertyGetMethod.ReturnType;
			Type delegateType = openGenericDelegateType.MakeGenericType(declaringType, returnType);
			Delegate target = propertyGetMethod.CreateDelegate(delegateType);
			return (Func<object, object>)openGenericWrapperMethod.MakeGenericMethod(declaringType, returnType).CreateDelegate(typeof(Func<object, object>), target);
		}

		public static Action<object, object> MakeFastPropertySetter(Type modelType, PropertyInfo propertyInfo)
		{
			MethodInfo setMethod = propertyInfo.SetMethod;
			ParameterExpression parameterExpression = Expression.Parameter(typeof(object), "x");
			ParameterExpression parameterExpression2 = Expression.Parameter(typeof(object), "y");
			TypeCode typeCode = Type.GetTypeCode(propertyInfo.PropertyType);
			UnaryExpression unaryExpression = Expression.Convert(Expression.Call(ChangeTypeMethod, parameterExpression2, Expression.Constant(typeCode)), propertyInfo.PropertyType);
			return Expression.Lambda<Action<object, object>>(Expression.Call(Expression.Convert(parameterExpression, modelType), setMethod, unaryExpression), new ParameterExpression[2]
			{
				parameterExpression,
				parameterExpression2
			}).Compile();
		}
	}
}
